/** @file    WoshHost.cpp
 *  @author  Alessandro Polo
 *  @version $Id: WoshHost.cpp 2833 2010-08-02 23:12:57Z alex $
 *  @brief
 * File containing methods for the wosh::WoshHost class.
 * The header for this class can be found in WoshHost.h, check that file
 * for class description.
 ****************************************************************************/
/* Copyright (c) 2007-2010, WOSH - Wide Open Smart Home 
 * by Alessandro Polo - OpenSmartHome.com
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 *     * Redistributions of source code must retain the above copyright
 *       notice, this list of conditions and the following disclaimer.
 *     * Redistributions in binary form must reproduce the above copyright
 *       notice, this list of conditions and the following disclaimer in the
 *       documentation and/or other materials provided with the distribution.
 *     * Neither the name of the OpenSmartHome.com WOSH nor the
 *       names of its contributors may be used to endorse or promote products
 *       derived from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY Alessandro Polo ''AS IS'' AND ANY
 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 * DISCLAIMED. IN NO EVENT SHALL Alessandro Polo BE LIABLE FOR ANY
 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 ****************************************************************************/
 
 #include <core/WoshHost.h>
 #include <core/Table.h>
 #include <core/Property.h>
 #include <core/Utilities.h>
 #include <core/DataModels.h>
 #include <core/List.h>
 #include <core/ObjectAllocator.h>


using namespace std;
namespace wosh {

 WOSH_REGISTER(wosh::WoshHost, "wosh::Object", 1.01, _static_WoshHost)

//////////////////////////////////////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////////////////////// CONSTRUCTORS

WoshHost::WoshHost()
	: Object(),
	hopsCount(-1),
	kernel_state(0),
	discoveryProtocolVersion(0),
	notifyFrequency(0),
	lastSeen(0),
	lastSeenRemote(0)
 { }

WoshHost::WoshHost( const std::string& WoshKernelName )
	: Object(NULL, WoshKernelName),
	hopsCount(-1),
	kernel_state(0),
	discoveryProtocolVersion(0),
	notifyFrequency(0),
	lastSeen(0),
	lastSeenRemote(0)
 { }

WoshHost::WoshHost( const WoshHost& m )
	: Object(m),
	discoverSource(m.discoverSource),
	discoverBindAddress(m.discoverBindAddress),
	hopsCount(m.hopsCount),
	kernel_state(m.kernel_state),
	discoveryProtocolVersion(m.discoveryProtocolVersion),
	notifyFrequency(m.notifyFrequency),
	lastSeen(m.lastSeen),
	lastSeenRemote(m.lastSeenRemote),
	protocols(m.protocols),
	connections(m.connections),
	neighbours(m.neighbours)
 { }

//////////////////////////////////////////////////////////////////////////////////////////////// CONSTRUCTORS
//////////////////////////////////////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////////////////////////// SETTERS

void WoshHost::clear()
 {
	this->discoverSource = "";
	this->discoverBindAddress = "";

	this->discoveryProtocolVersion = 0;

	this->hopsCount = -1;

	this->kernel_state = 0;

	this->notifyFrequency = 0;
	this->lastSeen = 0;
	this->lastSeenRemote = 0;

	this->protocols.clear();
	this->connections.clear();
 }

void WoshHost::setSeen( long time ) {
	if ( time == -1 )
		this->lastSeen = Utilities::std_time();
	else
		this->lastSeen = time;
 }

///////////////////////////////////////////////////////////////////////////////////////////////////// SETTERS
//////////////////////////////////////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////////////////////////// GETTERS

int WoshHost::isConnected() const {
	int count = 0;
	std::map<std::string,long>::const_iterator it;
	for ( it=this->connections.begin(); it!=this->connections.end(); ++it ) {
		if ( it->second == 0 ) continue;
		count++;
	 }
	return count;
 }

bool WoshHost::isTimedOut( long span_seconds ) const {
	if ( this->notifyFrequency <= 0  )
		return false;
	long expir = this->lastSeen + (long)this->notifyFrequency + span_seconds;
	if ( expir >= Utilities::std_time() )
		return false;
	return true;
 }

///////////////////////////////////////////////////////////////////////////////////////////////////// GETTERS
//////////////////////////////////////////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////////////////////////// OPERATORS

WoshHost& WoshHost::operator=( const WoshHost& m ) {
	if (this == &m) return *this; // same object?
	(Object&)*this = (const Object&)m;

	this->discoverSource = m.discoverSource;
	this->discoverBindAddress = m.discoverBindAddress;

	this->hopsCount = m.hopsCount;

	this->kernel_state = m.kernel_state;

	this->discoveryProtocolVersion = m.discoveryProtocolVersion;

	this->notifyFrequency = m.notifyFrequency;
	this->lastSeen = m.lastSeen;
	this->lastSeenRemote = m.lastSeenRemote;

	this->connections = m.connections;
	this->protocols = m.protocols;
	this->neighbours = m.neighbours;

	return *this;
 }

bool WoshHost::operator==( const WoshHost &other ) const
 {
	if ( getName() != other.getName() )
		return false;

	if ( this->discoverSource != other.discoverSource )
		return false;
	if ( this->discoverBindAddress != other.discoverBindAddress )
		return false;

	if ( this->kernel_state != other.kernel_state )
		return false;

	return true;
 }

/////////////////////////////////////////////////////////////////////////////////////////////////// OPERATORS
//////////////////////////////////////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////////// PROTOCOLS & CONNECTIONS

void WoshHost::setProtocolSupported( const std::string& value )
 {
	std::vector<std::string>::const_iterator itp;
	for ( itp=this->protocols.begin(); itp!=this->protocols.end(); ++itp ) {
		if ( *itp == value )
			return;
	 }
	this->protocols.push_back(value);
 }

//////////////////////////////////////////////////////////////////////////////////////////////////////////////

std::vector<std::string> WoshHost::getNeighboursName() const
 {
	std::vector<std::string> nnames;
	std::map<std::string, long>::const_iterator itp;
	for ( itp=this->neighbours.begin(); itp!=this->neighbours.end(); ++itp ) {
		nnames.push_back(itp->first);
	 }
	return nnames;
 }

///////////////////////////////////////////////////////////////////////////////////// PROTOCOLS & CONNECTIONS
//////////////////////////////////////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////////////////////////// HELPERS

Property* WoshHost::getProperty( const std::string& key ) const
 {
	Variant data;
	if ( key == _KEY_name )									data.set( this->getName() );
	else if ( key == _KEY_type )							data.set( this->getClassName() );
	else if ( key == _WoshHost_KEY_Seen )					{ data.set( this->lastSeen, Variant::DATETIME); }
	else if ( key == _WoshHost_KEY_SeenRemote )				{ data.set( this->lastSeenRemote, Variant::DATETIME); }

	else if ( key == _WoshHost_KEY_DiscoverSource )			data.set( this->discoverSource );
	else if ( key == _WoshHost_KEY_DiscoveryBindAddress )	data.set( this->discoverBindAddress );
	else if ( key == _WoshHost_KEY_DiscoveryProtocolVersion ) data.set( (int)this->discoveryProtocolVersion );
	else if ( key == _WoshHost_KEY_NotifyFrequency )		data.set( (int)this->notifyFrequency );

	else if ( key == _WoshHost_KEY_KernelState )			data.set( (int)this->kernel_state );

	else if ( key == _WoshHost_KEY_HopsCount )				data.set( this->hopsCount );

	else if ( key == _WoshHost_KEY_ProtocolCount )			data.set( (int)this->protocols.size() );
	else if ( key == _WoshHost_KEY_ConnectionCount )		data.set( this->isConnected() );

	else if ( key == _WoshHost_KEY_Protocols ) {
		std::string result = "";
		std::map<std::string,long>::const_iterator it;
		for ( it=this->connections.begin(); it!=this->connections.end(); ) {
			result += it->first;
			++it;
			if ( it!=this->connections.end() )
				result += "\n";
		 }
		data.set( result );
	 }
	else if ( key == _WoshHost_KEY_Connections ) {
		std::string result = "";
		std::map<std::string,long>::const_iterator it;
		for ( it=this->connections.begin(); it!=this->connections.end(); ) {
			if ( it->second == 0 ) { // not connected 
				++it;
				continue;
			 }
			result += Utilities::format("%s{%ld};", it->first.c_str(), it->second);
			++it;
			if ( it!=this->connections.end() )
				result += "\n";
		 }
		data.set( result );
	 }
	else if ( key == _WoshHost_KEY_Neighbours ) {
		std::string result = "";
		std::map<std::string,long>::const_iterator it;
		for ( it=this->neighbours.begin(); it!=this->neighbours.end(); ) {
			if ( it->second == 0 ) { // not connected 
				++it;
				continue;
			 }
			result += Utilities::format("%s{%ld};", it->first.c_str(), it->second);
			++it;
			if ( it!=this->neighbours.end() )
				result += "\n";
		 }
		data.set( result );
	 }

	else
		return NULL;
	return new Property(key, data, this->getPermission() );
 }

Table* WoshHost::getProperties() const
 {
	Table* table = new Table( 15, 2, true );
	table->setTableName("Properties");
	table->setHeaderHorizontal("Name", 0);
	table->setHeaderHorizontal("Value", 1);
	unsigned int iRow = 0;
	table->set<Variant>( _KEY_name, iRow, 0);
	table->set<Variant>( this->getName(), iRow, 1);
	++iRow;
	table->set<Variant>( _KEY_type, iRow, 0);
	table->set<Variant>( this->getClassName(), iRow, 1);
	++iRow;
	table->set<Variant>( _WoshHost_KEY_Seen, iRow, 0);
	table->set( new Variant(this->lastSeen, Variant::DATETIME), iRow, 1);
	++iRow;
	table->set<Variant>( _WoshHost_KEY_SeenRemote, iRow, 0);
	table->set( new Variant(this->lastSeenRemote, Variant::DATETIME), iRow, 1);
	++iRow;
	table->set<Variant>( _WoshHost_KEY_NotifyFrequency, iRow, 0);
	table->set<Variant>( (int)this->notifyFrequency, iRow, 1);
	++iRow;
	table->set<Variant>( _WoshHost_KEY_DiscoverSource, iRow, 0);
	table->set<Variant>( this->discoverSource, iRow, 1);
	++iRow;
	table->set<Variant>( _WoshHost_KEY_DiscoveryBindAddress, iRow, 0);
	table->set<Variant>( this->discoverBindAddress, iRow, 1);
	++iRow;
	table->set<Variant>( _WoshHost_KEY_DiscoveryProtocolVersion, iRow, 0);
	table->set( new Variant((int)this->discoveryProtocolVersion), iRow, 1);
	++iRow;
	table->set<Variant>( _WoshHost_KEY_KernelState, iRow, 0);
	table->set( new Variant((int)this->kernel_state), iRow, 1);
	++iRow;
	table->set<Variant>(_WoshHost_KEY_ProtocolCount, iRow, 0);
	table->set( new Variant((int)this->protocols.size()), iRow, 1);
	++iRow;
	table->set<Variant>( _WoshHost_KEY_ConnectionCount, iRow, 0);
	table->set( new Variant((int)this->connections.size()), iRow, 1);

	++iRow;
	table->set<Variant>( _WoshHost_KEY_Protocols, iRow, 0);
	Variant* data_proto = new Variant();
	std::string result_p = "";
	std::vector<std::string>::const_iterator it_p;
	for ( it_p=this->protocols.begin(); it_p!=this->protocols.end(); it_p++) {
		result_p += *it_p + ";";
	 }
	data_proto->set( result_p );
	table->set( data_proto, iRow, 1);

	++iRow;
	table->set<Variant>( _WoshHost_KEY_Connections, iRow, 0);
	Variant* data_conn = new Variant();
	std::string result_c = "";
	std::map<std::string,long>::const_iterator it_c;
	for ( it_c=this->connections.begin(); it_c!=this->connections.end(); it_c++) {
		result_c += Utilities::format("%s{%ld};", it_c->first.c_str(), it_c->second);
	 }
	data_conn->set( result_c );
	table->set( data_conn, iRow, 1);

	++iRow;
	table->set<Variant>( _WoshHost_KEY_Neighbours, iRow, 0);
	Variant* data_neigh = new Variant();
	std::string result_n = "";
	std::map<std::string,long>::const_iterator it_n;
	for ( it_n=this->neighbours.begin(); it_n!=this->neighbours.end(); it_n++) {
		result_n += Utilities::format("%s{%ld};", it_n->first.c_str(), it_n->second);
	 }
	data_neigh->set( result_n );
	table->set( data_neigh, iRow, 1);

	return table;
 }

///////////////////////////////////////////////////////////////////////////////////////////////////// HELPERS
//////////////////////////////////////////////////////////////////////////////////////////////////////////////

std::string WoshHost::toString() const {
	return Utilities::format("%s[%s]", getClassNameOnlyOf(getClassName()), getName().c_str() );
 }

bool WoshHost::updateHash( DataHashContext* hash_context ) const {
	if ( hash_context == NULL ) return false;
	bool ret = true;
	ret = hash_context->update( this->discoverSource ) & ret;
	ret = hash_context->update( this->discoverBindAddress ) & ret;
	ret = hash_context->update( this->hopsCount ) & ret;
	ret = hash_context->update( this->kernel_state ) & ret;
	ret = hash_context->update( this->discoveryProtocolVersion ) & ret;
	ret = hash_context->update( this->notifyFrequency ) & ret;
	ret = hash_context->update( this->lastSeen ) & ret;
	ret = hash_context->update( this->lastSeenRemote ) & ret;
	ret = hash_context->update( this->protocols ) & ret;
	ret = hash_context->update( this->connections ) & ret;
	ret = hash_context->update( this->neighbours ) & ret;
	return ret;
 }

//////////////////////////////////////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////////////////////////////////////

DataFieldCollector<WoshHost>* WoshHost::getDataFields() {
	DataFieldCollector<WoshHost>* fields = new DataFieldCollector<WoshHost>();
	fields->add<const std::string&, Variant>(&WoshHost::getName, "Name", 0);
	fields->add<const std::string&,Variant>(&WoshHost::getDiscoverySource, "Source", 1);
	fields->add<const std::string&,Variant>(&WoshHost::getDiscoveryBindAddress, "BindAddress", 2);
	fields->add<long,Variant_DT>(&WoshHost::getSeen, "LastSeen", 3);
	fields->add<int,Variant>(&WoshHost::isConnected, "Connected", 4);
//	fields->add<std::string,Variant>( Utilities::joinVector(host->getNeighboursName(), ";"), "Neighbours", 4);
	fields->add<const char*,Variant>(&WoshHost::getClassName, "Type", 5, false);
	fields->add<const std::string&,Variant>(&WoshHost::getEntityID, "EntityID", 6, false);
	fields->add<const URI&,URI>(&WoshHost::getURI, "URI", 7, false);
	fields->add<const Permission&,Permission>(&WoshHost::getPermission, "Permission", 8, false);
	return fields;
 }

//////////////////////////////////////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////////////////////////////////////

 WOSH_REGISTER(wosh::WoshHost_ListChanged, "wosh::Fact", 1.0, _static_WoshHost_ListChanged )

std::string WoshHost_ListChanged::getWoshHostName() const {
	const ISerializable* item = getItem();
	if ( item != NULL && item->isKindOf<WoshHost>() )
		return item->as<WoshHost>()->getName();
	const Variant* var = getItemKey();
	if ( var == NULL ) return "";
	return var->toString();
 }

//////////////////////////////////////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////////////////////////////////////

 WOSH_REGISTER(wosh::WoshHost_ConnectionChanged, "wosh::Fact", 1.0, _static_WoshHost_ConnectionChanged )

WoshHost_ConnectionChanged::WoshHost_ConnectionChanged( const std::string& hostname, int connection_state )
	: Fact( _WoshHost_EVENT_ConnectionChanged ) {
	List* args = List::createFrom<Variant>( hostname, (int)connection_state );
	setData(args);
 }

WoshHost_ConnectionChanged::WoshHost_ConnectionChanged( WoshHost* host, int connection_state )
	: Fact( _WoshHost_EVENT_ConnectionChanged ) {
	List* list = new List(2);
	list->push_back(host);
	list->push_back<Variant>(connection_state);
	setData(list);
 }

const WoshHost* WoshHost_ConnectionChanged::getWoshHost() const {
	if ( !hasData() ) return NULL;
	if ( getData()->isKindOf<WoshHost>() ) {
		return getData()->as<WoshHost>();
	 }
	if ( getData()->isKindOf<List>() ) {
		const List* list = getData()->as<List>();
		if ( list == NULL ) return NULL;
		if ( list->size() < 2 ) return NULL;
		if ( list->itemAt(0) == NULL ) return NULL;
		return list->itemAt(0)->as<WoshHost>();
	 }
	return NULL;
 }

std::string WoshHost_ConnectionChanged::getWoshHostName() const {
	if ( !hasData() ) return "";
	if ( content->isKindOf<Variant>() ) {
		const Variant* val = content->as<Variant>();
		if ( val == NULL ) return "";
		return val->toString();
	 }
	if ( getData()->isKindOf<List>() ) {
		const List* list = getData()->as<List>();
		if ( list == NULL ) return "";
		if ( list->size() < 1 ) return "";
		if ( list->itemAt(0) == NULL ) return "";
		if ( list->itemAt(0)->isKindOf<WoshHost>() ) {
			const WoshHost* host = list->itemAt(0)->as<WoshHost>();
			return host->getName();
		 }
		if ( list->itemAt(0)->isKindOf<Variant>() ) {
			const Variant* val = list->itemAt(0)->as<Variant>();
			if ( val == NULL ) return "";
			return val->toString();
		 }
	 }
	return "";
 }

int WoshHost_ConnectionChanged::getConnectionState() const {
	const Variant* var = getDataItem<Variant>(1);
	if ( var == NULL ) return 0;
	if ( var->isNumeric() ) return 0;
	return var->toInteger(0);
 }

std::string WoshHost_ConnectionChanged::toString() const {
	std::string hostname = getWoshHostName();
	if ( isHostConnected() )
		return hostname + " connected";
	else if ( isHostDisconnected() )
		return hostname + " disconnected";
	return hostname + Utilities::format(" unknown[%i]", getConnectionState());
 }

//////////////////////////////////////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////////////////////////////////////

}; // namespace wosh
